package com.store.orm.mysql.crud.impl;

import com.store.orm.mysql.MySQLUtil;
import com.store.orm.mysql.annotation.On;
import com.store.orm.mysql.crud.filter.Filtration;
import com.store.orm.mysql.crud.filter.Placement;
import com.store.orm.util.FieldUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.store.orm.common.KeyWord.*;

/**
 * 多表查询组件
 *
 * @author 宫静雨
 * @version 1.0
 */
public class MultiSelect {
    private static final Logger logger = LoggerFactory.getLogger(MultiSelect.class);
    private Connection connection;
    private MySQLUtil UTIL;

    public MultiSelect(MySQLUtil mySQLUtil) {
        this.UTIL = mySQLUtil;
        this.connection = mySQLUtil.getConnection();
    }

    /**
     * 有过滤条件多表查询
     *
     * @param master     主表
     * @param replicate  从表
     * @param placement  连接条件
     * @param filtration 过滤条件
     * @return 查询的数据
     */
    public List<Map<String, Object>> select(Class<?> master, Class<?> replicate,
                                            Placement placement, Filtration filtration) {
        Multi multi = new Multi(master, replicate, UTIL);
        String sql;
        switch (placement) {
            case LEFT:
                sql = multi.left().on().getSql();
                break;
            case RIGHT:
                sql = multi.right().on().getSql();
                break;
            case INNER:
                sql = multi.inner().on().getSql();
                break;
            default:
                logger.error("连接条件错误 {}", placement);
                return null;
        }
        if (filtration != null) {
            sql = sql + filtration.get();
        }
        List<Map<String, Object>> maps = null;
        try {
            ResultSet rs = connection.prepareStatement(sql).executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();
            maps = new ArrayList<>();
            while (rs.next()) {
                Map<String, Object> map = new HashMap<>();
                Object m = master.newInstance();
                Object r = replicate.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = rs.getObject(i + 1);
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    String mP = FieldUtil.hump(master.getSimpleName());
                    String rP = FieldUtil.hump(replicate.getSimpleName());
                    if (columnLabel.startsWith(mP)) {
                        Field field = master.getDeclaredField(columnLabel.substring(mP.length() + 1));
                        field.setAccessible(true);
                        field.set(m, columnValue);
                    } else {
                        Field field = replicate.getDeclaredField(columnLabel.substring(rP.length() + 1));
                        field.setAccessible(true);
                        field.set(r, columnValue);
                    }
                }
                map.put("m", m);
                map.put("r", r);
                maps.add(map);
            }
            logger.info(">>> {}", sql);
        } catch (Exception e) {
            logger.error("{}", e.getMessage());
        }
        return maps;
    }

    /**
     * 封装 SQL 帮助类
     */
    private static class Multi {
        private StringBuilder sb = new StringBuilder();
        private Class<?> master;
        private Class<?> replicate;
        private MySQLUtil UTIL;

        /**
         * 初始化数据，完成主表 SQL 的拼接
         *
         * @param master    主表
         * @param replicate 从表
         */
        public Multi(Class<?> master, Class<?> replicate, MySQLUtil UTIL) {
            this.master = master;
            this.replicate = replicate;
            this.UTIL = UTIL;

            String tableName = FieldUtil.hump(master.getSimpleName());
            sb.append(SELECT);
            Field[] fields = master.getDeclaredFields();
            for (Field field : fields) {
                String fieldName = field.getName();
                sb.append(tableName).append(".").append(UTIL.getFieldName(field)).append(AS).append(tableName)
                        .append("_").append(fieldName).append(COMMA);
            }
            sb.deleteCharAt(sb.length() - 1).append(BLANK).append(FROM);
            sb.append(MYSQL_QUOTES).append(tableName).append(MYSQL_QUOTES).append(AS).append(tableName);
        }

        /**
         * 左连接
         *
         * @return 当前操作对象
         */
        public Multi left() {
            String tableName = FieldUtil.hump(replicate.getSimpleName());
            Field[] fields = replicate.getDeclaredFields();
            String[] split = sb.toString().split("FROM");
            StringBuilder prefix = new StringBuilder(split[0]);
            for (Field field : fields) {
                String fieldName = field.getName();
                prefix.append(COMMA).append(tableName).append(".").append(UTIL.getFieldName(field))
                        .append(AS).append(tableName).append("_").append(fieldName);
            }
            String suffix = split[1] + LEFT_JOIN + MYSQL_QUOTES + tableName + MYSQL_QUOTES +
                    AS + tableName;
            sb.delete(0, sb.length()).append(prefix).append(BLANK).append(FROM).append(suffix);
            return this;
        }

        /**
         * 右连接
         *
         * @return 当前操作对象
         */

        public Multi right() {
            String tableName = FieldUtil.hump(replicate.getSimpleName());
            Field[] fields = replicate.getDeclaredFields();
            String[] split = sb.toString().split("FROM");
            StringBuilder prefix = new StringBuilder(split[0]);
            for (Field field : fields) {
                String fieldName = field.getName();
                prefix.append(COMMA).append(tableName).append(".").append(UTIL.getFieldName(field))
                        .append(AS).append(tableName).append("_").append(fieldName);
            }
            String suffix = split[1] + RIGHT_JOIN + MYSQL_QUOTES + tableName + MYSQL_QUOTES +
                    AS + tableName;
            sb.delete(0, sb.length()).append(prefix).append(BLANK).append(FROM).append(suffix);
            return this;
        }

        /**
         * 内连接
         *
         * @return 当前操作对象
         */
        public Multi inner() {
            String tableName = FieldUtil.hump(replicate.getSimpleName());
            Field[] fields = replicate.getDeclaredFields();
            String[] split = sb.toString().split("FROM");
            StringBuilder prefix = new StringBuilder(split[0]);
            for (Field field : fields) {
                String fieldName = field.getName();
                prefix.append(COMMA).append(tableName).append(".").append(UTIL.getFieldName(field))
                        .append(AS).append(tableName).append("_").append(fieldName);
            }
            String suffix = split[1] + INNER_JOIN + MYSQL_QUOTES + tableName + MYSQL_QUOTES +
                    AS + tableName;
            sb.delete(0, sb.length()).append(prefix).append(BLANK).append(FROM).append(suffix);
            return this;
        }

        /**
         * 关联字段
         *
         * @return 当前操作对象
         */
        public Multi on() {
            sb.append(ON);
            String m = FieldUtil.hump(master.getSimpleName());
            String r = FieldUtil.hump(replicate.getSimpleName());
            for (Field field : master.getDeclaredFields()) {
                if (field.isAnnotationPresent(On.class)) {
                    sb.append(m).append(".").append(UTIL.getFieldName(field));
                    break;
                }
            }
            sb.append(" = ");
            for (Field field : replicate.getDeclaredFields()) {
                if (field.isAnnotationPresent(On.class)) {
                    sb.append(r).append(".").append(UTIL.getFieldName(field));
                    break;
                }
            }
            return this;
        }

        /**
         * 获取拼接的 SQL
         *
         * @return 当前操作对象
         */
        public String getSql() {
            return sb.toString();
        }
    }
}
